package org.yinxianren.java.test.thread2;

import java.util.concurrent.Semaphore;

/**
 * Thread0: Going to print a job
 * Thread9: Going to print a job
 * Thread8: Going to print a job
 * Thread7: Going to print a job
 * Thread6: Going to print a job
 * Thread5: Going to print a job
 * Thread3: Going to print a job
 * Thread4: Going to print a job
 * Thread2: Going to print a job
 * Thread1: Going to print a job
 * Thread0: PrintQueue: Printing a Job during 6 seconds
 * Thread0: The document has been printed
 * Thread9: PrintQueue: Printing a Job during 2 seconds
 * Thread9: The document has been printed
 * Thread8: PrintQueue: Printing a Job during 6 seconds
 * Thread8: The document has been printed
 * Thread7: PrintQueue: Printing a Job during 2 seconds
 * Thread7: The document has been printed
 * Thread6: PrintQueue: Printing a Job during 1 seconds
 * Thread6: The document has been printed
 * Thread5: PrintQueue: Printing a Job during 0 seconds
 * Thread5: The document has been printed
 * Thread3: PrintQueue: Printing a Job during 8 seconds
 * Thread3: The document has been printed
 * Thread4: PrintQueue: Printing a Job during 9 seconds
 * Thread4: The document has been printed
 * Thread2: PrintQueue: Printing a Job during 9 seconds
 * Thread2: The document has been printed
 * Thread1: PrintQueue: Printing a Job during 2 seconds
 * Thread1: The document has been printed
 *
 *
 * 这个例子的关键是PrintQueue类的printJob()方法。此方法展示了3个你必须遵守的步骤当你使用semaphore来实现critical section时，并保护共享资源的访问：
 *
 * 1. 首先， 你要调用acquire()方法获得semaphore。
 * 2. 然后， 对共享资源做出必要的操作。
 * 3. 最后， 调用release()方法来释放semaphore。
 *
 * 另一个重点是PrintQueue类的构造方法和初始化Semaphore对象。你传递值1作为此构造方法的参数，
 * 那么你就创建了一个binary semaphore。初始值为1，就保护了访问一个共享资源，在例子中是print queue。
 *
 * 当你开始10个threads，当你开始10个threads时，那么第一个获得semaphore的得到critical section的访问权。
 * 剩下的线程都会被semaphore阻塞直到那个获得semaphore的线程释放它。当这情况发生，
 * semaphore在等待的线程中选择一个并给予它访问critical section的访问权。全部的任务都会打印文档，
 * 只是一个接一个的执行。
 *
 * 更多…
 *
 * Semaphore类有另2个版本的 acquire() 方法：
 *
 * acquireUninterruptibly()：acquire()方法是当semaphore的内部计数器的值为0时，阻塞线程直到semaphore被释放。
 * 在阻塞期间，线程可能会被中断，然后此方法抛出InterruptedException异常。
 * 而此版本的acquire方法会忽略线程的中断而且不会抛出任何异常。
 * tryAcquire()：此方法会尝试获取semaphore。如果成功，返回true。如果不成功，返回false值，
 * 并不会被阻塞和等待semaphore的释放。接下来是你的任务用返回的值执行正确的行动。
 * Semaphores的公平性
 *
 * fairness的内容是指全java语言的所有类中，那些可以阻塞多个线程并等待同步资源释放的类（例如，semaphore)。
 * 默认情况下是非公平模式。在这个模式中，当同步资源释放，就会从等待的线程中任意选择一个获得资源，
 * 但是这种选择没有任何标准。而公平模式可以改变这个行为并强制选择等待最久时间的线程。
 *
 * 随着其他类的出现，Semaphore类的构造函数容许第二个参数。这个参数必需是 Boolean 值。
 * 如果你给的是 false 值，那么创建的semaphore就会在非公平模式下运行。如果你不使用这个参数，
 * 是跟给false值一样的结果。如果你给的是true值，那么你创建的semaphore就会在公平模式下运行。
 */
public class DemoSemaphore {
    public static void main(String[] args) {
        PrintQueue printQueue=new PrintQueue();

        Thread[] thread = new Thread[10];
        for (int i=0; i<10; i++){
            thread[i]=new Thread(new Job(printQueue),"Thread"+i);
        }
        for (int i=0; i<10; i++){
            thread[i].start();
        }

    }
}
class PrintQueue {
    private final Semaphore semaphore;
    public PrintQueue(){
        semaphore=new Semaphore(1);
    }
    public void printJob (Object document){

        try {
            semaphore.acquire();
            long duration=(long)(Math.random()*10);
            System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n",Thread.currentThread().getName(),duration);
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
    }
}

class Job implements Runnable {
    private PrintQueue printQueue;
    public Job(PrintQueue printQueue){
        this.printQueue=printQueue;
    }

    @Override
    public void run() {
        System.out.printf("%s: Going to print a job\n",Thread. currentThread().getName());
        printQueue.printJob(new Object());
        System.out.printf("%s: The document has been printed\n",Thread.currentThread().getName());
    }
}
